package multipart

import (
	"encoding/xml"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"

	"jdfs2/internal/api/common"
	mp "jdfs2/internal/multipart"
	"jdfs2/internal/service"
	"jdfs2/internal/utils"
)

// Multipart XML payloads
type InitiateMultipartUploadResult struct {
	XMLName  xml.Name `xml:"InitiateMultipartUploadResult"`
	Bucket   string   `xml:"Bucket"`
	Key      string   `xml:"Key"`
	UploadID string   `xml:"UploadId"`
}

type CompleteMultipartUpload struct {
	XMLName xml.Name        `xml:"CompleteMultipartUpload"`
	Parts   []CompletedPart `xml:"Part"`
}

type CompletedPart struct {
	PartNumber int    `xml:"PartNumber"`
	ETag       string `xml:"ETag"`
}

type CompleteMultipartUploadResult struct {
	XMLName  xml.Name `xml:"CompleteMultipartUploadResult"`
	Location string   `xml:"Location,omitempty"`
	Bucket   string   `xml:"Bucket"`
	Key      string   `xml:"Key"`
	ETag     string   `xml:"ETag"`
}

// RegisterRoutes registers multipart upload routes
func RegisterRoutes(api *gin.RouterGroup, s3svc service.S3Service) {
	// Initiate or Complete multipart
	api.POST("/:bucket/*key", multipartPost(s3svc))
}

// multipartPost handles POST /:bucket/*key for initiate/complete multipart
func multipartPost(s3svc service.S3Service) gin.HandlerFunc {
	return func(c *gin.Context) {
		bucket := c.Param("bucket")
		key := strings.TrimPrefix(c.Param("key"), "/")

		// Initiate multipart upload
		if _, ok := c.GetQuery("uploads"); ok {
			uploadID, err := s3svc.InitiateMultipart(c, bucket, key)
			if err != nil {
				if err == utils.ErrNoSuchBucket {
					common.WriteS3Error(c, http.StatusNotFound, "NoSuchBucket", "The specified bucket does not exist.")
				} else {
					common.WriteS3Error(c, http.StatusInternalServerError, "InternalError", err.Error())
				}
				return
			}
			resp := InitiateMultipartUploadResult{Bucket: bucket, Key: key, UploadID: uploadID}
			c.Header("Content-Type", "application/xml")
			_ = xml.NewEncoder(c.Writer).Encode(resp)
			return
		}

		// Complete multipart upload
		if uploadID, ok := c.GetQuery("uploadId"); ok {
			var req CompleteMultipartUpload
			if err := xml.NewDecoder(c.Request.Body).Decode(&req); err != nil {
				common.WriteS3Error(c, http.StatusBadRequest, "MalformedXML", "The XML you provided was not well-formed or did not validate against our published schema")
				return
			}
			var parts []mp.CompletePart
			for _, p := range req.Parts {
				parts = append(parts, mp.CompletePart{PartNumber: p.PartNumber, ETag: strings.Trim(p.ETag, "\"")})
			}
			etag, err := s3svc.CompleteMultipart(c, bucket, key, uploadID, parts)
			if err != nil {
				common.WriteS3Error(c, http.StatusBadRequest, "InvalidPart", err.Error())
				return
			}
			resp := CompleteMultipartUploadResult{Bucket: bucket, Key: key, ETag: "\"" + etag + "\""}
			c.Header("Content-Type", "application/xml")
			_ = xml.NewEncoder(c.Writer).Encode(resp)
			return
		}

		common.WriteS3Error(c, http.StatusNotImplemented, "NotImplemented", "Use ?uploads or ?uploadId for MPU")
	}
}
